/// @file BuildFlags.hh
/// @brief A series of flags to control extra output
///
/// Compile with:
///    - DDBG   To activate debug output
///    - DTRC   To activate code tracing
///    - DPERF  To activate performance monitoring output (Linux and OS/x only)
///
/// Within the code use:
///     - DB() {whatever}; To perform whatever (usually cerr output) when DBG set
///     - TR() {whatever}; To perform whatever (usually cerr output) when TRC set
///     - TRPRT(whatever); To output whatever via err with a header
///     - PRF(tracevar, "where"); To output performance info to stderr
///
/// Allocate a trace variable (tvar above) with:<br>
///     PerfVar myvar("name");
///  
/// You can also use the following for performance trace output:<br>
///    if (PerfTrace::on) cerr << PerfStream(tracevar, "where") << endl;
///
/// It is assumed that the compiler will not include code that is inactive 
/// at compile time. This should always be true at higher optimization levels
/// but may not be the case when optimization is turned off.

// *************************************************************************

// Copyright© 2010 Texas A&M University, 
//                 College Station, Texas
//                 Contact: R Crosby <rwc@cs.tamu.edu>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// *************************************************************************

#ifndef BUILDFLAGS_HH
#define BUILDFLAGS_HH

#include <fstream>
#include <iomanip>
#include <assert.h>

#include "PerfBlock.hh"

// *************************************************************************

namespace BuildFlags {

    static const int dbg =			  // Debug flag
#if DBG
       1;
#else
       0;
#endif
    
    static const int trc =			  // Trace flag
#if TRC
       1;
#else
       0;
#endif

    static const int perf=			  // Performance Monitor flag
#if PERF
       1;
#else
       0;
#endif

}

// *************************************************************************

/// @brief Do whatever if the debug flag is set
///
/// Since the flag is a constant, the compiler shouldn't generate anything
/// if optimization is on

#define DB()      if (BuildFlags::dbg)

/// @brief Do whatever if the trace flag is set
///
/// Since the flag is a constant, the compiler shouldn't generate anything
/// if optimization is on

#define TR()      if (BuildFlags::trc)

/// @brief Output a trace line.
///
/// Since the flag is a constant, the compiler shouldn't generate anything
/// if optimization is on

#define TRPRT(x)  TR() cerr << "Trc: " << x << endl << flush;

/// @brief Output a line of performance data.
///
/// Since the flag is a constant, the compiler shouldn't generate anything
/// if optimization is on

#define PRF(v, t) if (BuildFlags::perf) cerr << PerfStream(v, t) << endl << flush;

// *************************************************************************
/// @brief Performance monitor class
///
/// Defines a named monitor. Multiple monitors with different names
/// can be defined.

class PerfVar {

private:
    const char *  title;                  // Title for outputs 

    ostream & print(ostream &os) const {

	PerfBlock pb;

	assert ( !GetPerformanceInfo (&pb) );

	os << " " << pb.userTime.tv_sec << '.' 
	   << setw(6) << setfill('0') << pb.userTime.tv_usec         // User cpu time
	   << " " << pb.sysTime.tv_sec 
	   << '.' << setw(6) << setfill('0') << pb.sysTime.tv_usec   // System cpu time

	   // The following are only calculated depending on the os
	   << " " << pb.resPrivate             // Resident private size 
	   << " " << pb.virPrivate             // Private virtual size
	   << " " << pb.virSize                // Virtual size (private + shared)

	   // The following are only populated in linux (2.6 and beyond)
	   << " " << pb.minorFaults            // Minor page faults (No I/O)
	   << " " << pb.majorFaults            // Major page faults (I/O)
	   << " " << pb.volContextSw           // Voluntary context switches
	   << " " << pb.involContextSw         // Involuntary context switches

	   << " " << setfill(' ');

          return os;

     }               

     friend ostream & operator<< (ostream &       os, 
                                  const PerfVar & t);

     friend class PerfStream;

public:

  PerfVar(const char * t) : title(t) {};

};
     
// *************************************************************************
/// @brief Operator to output a line of performance data

inline ostream & operator<<( ostream &       os, 
                             const PerfVar & t) 
{ 
     return t.print(os); 
}

// *************************************************************************
/// @brief Helper class to display the leading columns of the output
///
/// This class should never be instantiated by the user but will be
/// instantiated but the PRF() macro. 

class PerfStream {
private:
     const char *    where;
     const PerfVar & who;

     ostream & print(ostream &os) const {
          os << "Perf: " << who.title
             << " " << where 
             << who;
          return os;
     }    
           
     friend ostream & operator<<(ostream           &os, 
                                 const PerfStream  &t);

public:
     PerfStream( PerfVar &    h,
                 const char * w ) 
          : where(w), who(h) {};

};

// *************************************************************************
/// @brief Operator to output the performance header

inline ostream & operator<<( ostream           &os, 
                             const PerfStream  &t) 
{ 
     return t.print(os); 
}

#endif	// BUILDFLAGS_HH
